<template>
	<div class="lb-pull-refresh" ref="scrollRoot">
		<div
			class="lb-pull-refresh__track"
			:style="trackStyle"
			@touchstart="onTouchStart"
			@touchmove="onTouchMove"
			@touchend="onTouchEnd"
			@touchcancel="onTouchend"
		>
			<div :style="getHeadStyle" class="lb-pull-refresh__head">
				<div v-if="status === 'loading'" class="loading"></div>
				<div v-else class="loading-icons"></div>
				<div v-if="TEXT_STATUS.includes(status)" class="pull-text">{{ getStatusText() }}</div>
				<div v-if="status === 'loading'" class="pull-text-loading">{{ t(loadingText) }}</div>
			</div>
			<slot></slot>
		</div>
	</div>
</template>

<script>
import { ref, watch, reactive, nextTick, computed, defineComponent, toRefs, onMounted } from 'vue';
import { useI18n } from "vue-i18n";
import useScrollParent from '@/hooks/pullRefresh/useScrollParent';
import useTouch from '@/hooks/pullRefresh/useTouch';
const DEFAULT_HEAD_HEIGHT = 50;

function getScrollTop(el) {
	const top = 'scrollTop' in el ? el.scrollTop : el.pageYOffset;
	return Math.max(top, 0);
}

function preventDefault(event, isStopPropagation) {
	if (typeof event.cancelable !== 'boolean' || event.cancelable) {
		event.preventDefault();
	}
	if (isStopPropagation) {
		stopPropagation(event);
	}
}

export default defineComponent({
	name: 'pull-refresh',
	props: {
		disabled: Boolean,
		successText: {
			type: String,
			default: 'home_loading_success',
		},
		pullingText: {
			type: String,
			default: 'home_loading_pull',
		},
		loosingText: {
			type: String,
			default: 'home_loading_release',
		},
		loadingText: {
			type: String,
			default: 'home_loading',
		},
		pullDistance: [Number, String],
		modelValue: {
			type: Boolean,
			default: false,
		},
		successDuration: {
			type: [Number, String],
			default: 500,
		},
		animationDuration: {
			type: [Number, String],
			default: 300,
		},
		headHeight: {
			type: [Number, String],
			default: DEFAULT_HEAD_HEIGHT,
		},
		body: {
			type: String,
			default: ''
		}
	},

	emits: ['refresh', 'update:modelValue'],

	setup(props, { emit, slots }) {
		const { t } = useI18n();
		let reachTop = false;
		const TEXT_STATUS = ['pulling', 'loosing', 'success'];

		let scrollParent;
		let root = ref(null);
		const scrollRoot = ref(null);
		if (props.body) {
			root.value = document.getElementById(props.body);
		} else {
			root.value = document.body;
		}
		if (root.value) {
			scrollParent = useScrollParent(root.value)
		}
		
		const state = reactive({
			status: 'normal',
			distance: 0,
			duration: 0,
		});

		const trackStyle = ref({});

		const touch = useTouch();

		const getHeadStyle = computed(() => {
			if (props.headHeight !== DEFAULT_HEAD_HEIGHT) {
				return {
					height: `${props.headHeight}px`,
				};
			}
			return {};
		});

		const isTouchable = () => state.status !== 'loading' && state.status !== 'success' && !props.disabled;

		const ease = (distance) => {
			const pullDistance = +(props.pullDistance || props.headHeight);

			if (distance > pullDistance) {
				if (distance < pullDistance * 2) {
					distance = pullDistance + (distance - pullDistance) / 2;
				} else {
					distance = pullDistance * 1.5 + (distance - pullDistance * 2) / 4;
				}
			}

			return Math.round(distance);
		};

		const setTrackStyle = () => {
			return {
				transitionDuration: `${state.duration}ms`,
				transform: state.distance ? `translate3d(0,${state.distance}px, 0)` : '',
			};
		};

		const setStatus = (distance, isLoading) => {
			const pullDistance = +(props.pullDistance || props.headHeight);
			state.distance = distance;

			if (isLoading) {
				state.status = 'loading';
			} else if (distance === 0) {
				state.status = 'normal';
			} else if (distance < pullDistance) {
				state.status = 'pulling';
			} else {
				state.status = 'loosing';
			}
			trackStyle.value = setTrackStyle();
		};

		const getStatusText = () => {
			const { status } = state;
			if (status === 'normal') {
				return '';
			}
			return t(props[`${status}Text`]) || '';
		};

		const showSuccessTip = () => {
			state.status = 'success';

			setTimeout(() => {
				setStatus(0);
			}, +props.successDuration);
		};

		const checkPosition = (event) => {
			console.log('checkPosition', scrollParent.value)
			reachTop = getScrollTop(scrollParent.value) === 0;

			if (reachTop) {
				state.duration = 0;
				touch.start(event);
			}
		};

		const onTouchStart = (event) => {
			if (isTouchable()) {
				checkPosition(event);
			}
		};

		const onTouchMove = (event) => {
			if (isTouchable()) {
				if (!reachTop) {
					checkPosition(event);
				}

				const { deltaY } = touch;
				touch.move(event);

				if (reachTop && deltaY.value >= 0 && touch.isVertical()) {
					preventDefault(event);
					setStatus(ease(deltaY.value));
				}
			}
		};

		const onTouchEnd = () => {
			if (reachTop && touch.deltaY.value && isTouchable()) {
				state.duration = +props.animationDuration;

				if (state.status === 'loosing') {
					setStatus(+props.headHeight, true);
					emit('update:modelValue', true);

					// ensure value change can be watched
					nextTick(() => emit('refresh'));
				} else {
					setStatus(0);
				}
			}
		};

		onMounted(() => {
			nextTick(() => {
				if (!scrollParent) {
					const _root = document.getElementById(props.body);
					root.value = _root;	
					scrollParent = root
				}
			})
		});

		watch(
			() => props.modelValue,
			(value) => {
				state.duration = +props.animationDuration;

				if (value) {
					setStatus(+props.headHeight, true);
				} else if (slots.success || props.successText) {
					showSuccessTip();
				} else {
					setStatus(0, false);
				}
			}
		);

		return {
			...toRefs(state),
			t,
			root,
			scrollRoot,
			trackStyle,
			TEXT_STATUS,
			getHeadStyle,
			getStatusText,
			onTouchStart,
			onTouchMove,
			onTouchEnd,
		};
	},
});
</script>

<style>
:root {
	--lb-pull-refresh-head-height: 50px;
	--lb-pull-refresh-head-font-size: 12px;
	--lb-pull-refresh-head-text-color: #AFB8CB;
	--lb-pull-refresh-loading-icon-size: 16px;
}

.lb-pull-refresh {
	overflow: hidden;
	user-select: none;
	height: 100%;
}
.lb-pull-refresh__track {
	position: relative;
	height: 100%;
	transition-property: transform;
}

.lb-pull-refresh__head {
	position: absolute;
	left: 0;
	width: 100%;
	height: var(--lb-pull-refresh-head-height);
	overflow: hidden;
	color: var(--lb-pull-refresh-head-text-color);
	font-size: var(--lb-pull-refresh-head-font-size);
	/* line-height: var(--lb-pull-refresh-head-height); */
	line-height: 25px;
	text-align: center;
	transform: translateY(-100%);
	
}
.lb-pull-refresh__head .loading-icons {
	width: 224px;
	height: 21px;
	background: url('/imgs/loading_bg.png') no-repeat;
	background-size: 100% 100%;
	margin: 0 auto;
}
.lb-pull-refresh__head .loading{
	width: 21px;
	height: 21px;
	background: url('/imgs/loading_icon.png') no-repeat;
	background-size: 100% 100%;
	margin: 0 auto;
	animation: loading-rotate 1s linear infinite;
}
@keyframes loading-rotate {
	from {
		transform: rotate(0deg);
	}
	to {
		transform: rotate(360deg);
	}
}
</style>
